package com.sanhuo.idms;

import com.sanhuo.MapperProxy;
import com.sanhuo.idms.dao.DeviceStateMapper;
import com.sanhuo.idms.dao.DormElectricMapper;
import com.sanhuo.idms.dao.DormIpMapper;
import com.sanhuo.idms.entity.DeviceState;
import com.sanhuo.idms.entity.Dorm;
import com.sanhuo.idms.entity.DormElectric;
import com.sanhuo.idms.entity.DormIp;
import com.sanhuo.triild.Session;
import com.sanhuo.utils.basic.StringUtil;

import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

public class SocketServer {
    private static Logger log = Logger.getLogger(SocketServer.class.getName());

    //解码buffer
    private Charset cs = Charset.forName("UTF-8");
    //接受数据缓冲区
    private static ByteBuffer sBuffer = ByteBuffer.allocate(1024);
    //发送数据缓冲区
    private static ByteBuffer rBuffer = ByteBuffer.allocate(1024);
    //选择器（叫监听器更准确些吧应该）
    public static Selector selector;


    public static Map<String, SocketChannel> map = new ConcurrentHashMap<>();

    public final static String V = "voltage";
    public final static String I = "electricity";
    public final static String P = "power";
    public final static String E = "energy";

    private DormIpMapper dormIpMapper = MapperProxy.ProxyFactory(DormIpMapper.class);
    private DormElectricMapper dormElectricMapper = MapperProxy.ProxyFactory(DormElectricMapper.class);
    private DeviceStateMapper deviceStateMapper = MapperProxy.ProxyFactory(DeviceStateMapper.class);

    /**
     * 启动socket服务，开启监听
     *
     * @param port
     * @throws IOException
     */
    public void startSocketServer(int port) {
        try {
            //打开通信信道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            //获取套接字
            ServerSocket serverSocket = serverSocketChannel.socket();
            //绑定端口号
            serverSocket.bind(new InetSocketAddress(port));
            //打开监听器
            selector = Selector.open();
            //将通信信道注册到监听器
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            //监听器会一直监听，如果客户端有请求就会进入相应的事件处理
            while (true) {
                selector.select();//select方法会一直阻塞直到有相关事件发生或超时
                Set<SelectionKey> selectionKeys = selector.selectedKeys();//监听到的事件
                for (SelectionKey key : selectionKeys) {
                    //具体的执行函数
                    handle(key);
                }
                selectionKeys.clear();//清除处理过的事件
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理不同的事件
     *
     * @param selectionKey
     * @throws IOException
     */
    private void handle(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel serverSocketChannel = null;
        SocketChannel socketChannel = null;
        String requestMsg = "";
        String sendText = "";
        try {
            int count = 0;

            //ip+端口为唯一识别
            if (selectionKey.isAcceptable()) {
                //每有客户端连接，即注册通信信道为可读
                serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                socketChannel = serverSocketChannel.accept();
                //设置为非阻塞
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
            } else if (selectionKey.isReadable()) {
                //获取socket通道
                socketChannel = (SocketChannel) selectionKey.channel();
                //清空读取数据缓存区
                rBuffer.clear();
                //读取数据
                count = socketChannel.read(rBuffer);
                //读取数据
                if (count > 0) {
                    rBuffer.flip();
                    /*
                    满足im1281B的数据帧格式
                    */
                    if (Integer.toHexString(rBuffer.get(0) & 0xFF).equals("1")
                            && Integer.toHexString(rBuffer.get(1) & 0xFF).equals("3")
                            && Integer.toHexString(rBuffer.get(2) & 0xFF).equals("20")) {
                        for (int i = 0; i < count; i++) {
                            requestMsg += (Integer.toHexString(rBuffer.get(i) & 0xFF) + " ");
                        }
                        //获取电压电流功率数据
                        Map<String, Double> data = getData(rBuffer);
                        Double vio = data.get(V);
                        Double i = data.get(I);
                        //通过ip和port的socket套接字找到对应的宿舍
                        String ip = socketChannel.socket().getInetAddress().getHostAddress();
                        int port = socketChannel.socket().getPort();
                        ip += port;
                        DormIp dormIp = dormIpMapper.findByIp(ip);
//                        System.out.println(vio);
                        if (dormIp != null) {
                            //更新宿舍消耗电量
                            String dormId = dormIp.getDormId();
                            DormElectric dormElectric = dormElectricMapper.findByDorm(dormId);
                            if (dormElectric != null) {
                                double consume = data.get(E);
                                dormElectric.setConsume(consume);
                                dormElectric.setUpdateTime(new Date());
                                dormElectricMapper.update(dormElectric);
                            }
                            log.info("宿舍编号:" + dormIp.getDormId() + "传来本次电能消耗为:" +  data.get(E));
                        }

                    } else {
                        requestMsg = String.valueOf(cs.decode(rBuffer).array());
//                        System.out.println(requestMsg);
                    /*
                     带有"dormId:"说明是连接
                     */
                        if (requestMsg.contains("dormId")) {
                            String ip = socketChannel.socket().getInetAddress().getHostAddress();
                            int port = socketChannel.socket().getPort();
                            ip += port;
                            //通过ip+端口的套接字为key,socketChannel为value存放到内存中
                            map.put(ip, socketChannel);
                            log.info("有客户端:" + ip + " 连接端口");
                            String dormId = requestMsg.substring(requestMsg.lastIndexOf(":") + 1, requestMsg.lastIndexOf(":") + 33);
                            DormIp dormIp = dormIpMapper.findByDormId(dormId);
                            if (dormIp == null) {
                                dormIp = new DormIp();
                                dormIp.setId(StringUtil.UUID());
                                dormIp.setDormId(dormId);
                                dormIp.setIp(ip);
                                dormIp.setUpdateTime(new Date());
                                dormIpMapper.save(dormIp);
                            } else {
                                SocketChannel oriChannel = map.get(dormIp.getIp());
                                if (oriChannel != null) {
                                    oriChannel.close();
                                }
                                log.info("关闭旧ip:" + dormIp.getIp() + " 连接");
                                dormIp.setIp(ip);
                                dormIp.setDormId(dormId);
                                dormIp.setUpdateTime(new Date());
                                dormIpMapper.update(dormIp);
                            }
                        }
                        /*
                            更新电灯状态
                         */
                        if (requestMsg.contains("light:")) {
                            String state
                                    = requestMsg.substring(requestMsg.lastIndexOf(":") + 1, requestMsg.lastIndexOf(":") + 2);
                            System.out.println("state:" + state);
                            String ip = socketChannel.socket().getInetAddress().getHostAddress();
                            int port = socketChannel.socket().getPort();
                            ip += port;
                            DormIp dormIp = dormIpMapper.findByIp(ip);
                            if (dormIp != null) {
                                DeviceState device = deviceStateMapper.findByDormId(dormIp.getDormId());
                                if (device != null) {
                                    if (state.equals("1")) {
                                        device.setState(true);
                                        log.info("宿舍编号:" + dormIp.getDormId() + "电灯为打开状态");
                                    } else {
                                        device.setState(false);
                                        log.info("宿舍编号:" + dormIp.getDormId() + "电灯为关闭状态");
                                    }
                                    device.setUpdateTime(new Date());
                                    deviceStateMapper.update(device);
                                } else {
                                    device = new DeviceState();
                                    device.setId(StringUtil.UUID());
                                    device.setUpdateTime(new Date());
                                    if (state.equals("1")) {
                                        device.setState(true);
                                        log.info("宿舍编号:" + dormIp.getDormId() + "电灯为打开状态");

                                    } else {
                                        device.setState(false);
                                        log.info("宿舍编号:" + dormIp.getDormId() + "电灯为关闭状态");
                                    }
                                    device.setDormId(dormIp.getDormId());
                                    device.setDevice("light");
                                    deviceStateMapper.save(device);
                                }
                            }
                        }
                    }
                } else {
                    //小于0说明断开了连接
                    selectionKey.cancel();
                    //关闭对应的socket通道
                    socketChannel.close();
                    //移除map中的数据
                    String ip = socketChannel.socket().getInetAddress().getHostAddress();
                    int port = socketChannel.socket().getPort();
                    ip += port;
                    map.remove(ip);
                    log.info("有客户端:" + ip + " 断开连接");
                }
                socketChannel.register(selector, SelectionKey.OP_READ);
            }
        } catch (IOException e) {
            //如果捕获到该SelectionKey对应的Channel时出现了异常,即表明该Channel对于的Client出现了问题
            //所以从Selector中取消该SelectionKey的注册
            selectionKey.cancel();
            if (selectionKey.channel() != null) {
                try {
                    selectionKey.channel().close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
    /*
        IM1281B传来的数据帧为
        01 03 20 (1~3为数据帧开头标识)
        00 21 8D D8 (4~7位为电流的16进制表示)
        00 01 38 75 (8~11位为电压的16进制表示)
        01 0C 63 08 (12~15位为功率的16进制表示)
        00 00 00 5A (16~19位为电能的16进制表示)
        00 00 03 E8 (20~23位为功率因数的16进制表示)
        00 00 00 59 (24~27位为二氧化碳的16进制表示)
        00 00 0C CB (28~32位为频率的16进制表示)
        00 00 13 88 (33~36为仪表地址的16进制表示)
        1B C2 (最后两位数据帧的结束标志)

     */
    public Map<String, Double> getData(ByteBuffer rBuffer) {
        //电压的16进制字符串
        //tcp传输的数据为二进制,先通过二进制转16进制再转为10进制得到数据
        //rBuffer为tcp读取的数据
        String voltageHex = Integer.toHexString(rBuffer.get(3) & 0xFF) +
                Integer.toHexString(rBuffer.get(4) & 0xFF) +
                Integer.toHexString(rBuffer.get(5) & 0xFF) +
                Integer.toHexString(rBuffer.get(6) & 0xFF);
        //电流的16进制字符串
        String electricityHex = Integer.toHexString(rBuffer.get(7) & 0xFF) +
                Integer.toHexString(rBuffer.get(8) & 0xFF) +
                Integer.toHexString(rBuffer.get(9) & 0xFF) +
                Integer.toHexString(rBuffer.get(10) & 0xFF);
        //功率的16进制字符串
        String powerHex = Integer.toHexString(rBuffer.get(11) & 0xFF) +
                Integer.toHexString(rBuffer.get(12) & 0xFF) +
                Integer.toHexString(rBuffer.get(13) & 0xFF) +
                Integer.toHexString(rBuffer.get(14) & 0xFF);
        //电能的16进制字符串
        String energyHex = Integer.toHexString(rBuffer.get(15) & 0xFF) +
                Integer.toHexString(rBuffer.get(16) & 0xFF) +
                Integer.toHexString(rBuffer.get(17) & 0xFF) +
                Integer.toHexString(rBuffer.get(18) & 0xFF);
        //16转10进制再/10000是实际的值,电压的单位是V,电流是A,功率是W,电能是KWH
        Double voltage = (double) Long.parseLong(voltageHex, 16) / 10000;
        Double electricity = (double) Long.parseLong(electricityHex, 16) / 10000;
        Double power = (double) Long.parseLong(powerHex, 16) / 10000;
        Double energy = (double) Long.parseLong(energyHex, 16) / 10000;
        Map<String, Double> data = new HashMap<>();
        data.put(V, voltage);
        data.put(I, electricity);
        data.put(P, power);
        data.put(E, energy);
        return data;
    }

//    public static void main(String[] args) {
//        double dec_num = (double) Long.parseLong("009858", 16) / 10000;
//        System.out.println(dec_num);
//    }
}
